package com.estock.platform.manager;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.transaction.annotation.Transactional;

import com.estock.common.consts.DaoQueryConsts;
import com.estock.common.dao.BaseDaoSupport;
import com.estock.common.model.security.BaseEntity;
import com.estock.common.model.security.Stock;
import com.estock.common.model.task.Task;
import com.estock.common.util.AppCtx;
import com.estock.common.util.CommonUtil;

/**
 * Should introduce DAO if thing goes complicated.
 * 
 * @author richard
 * 
 */
public class DBManager extends BaseDaoSupport {

	private static DBManager instance;

	// set it as public for transaction bug
	public DBManager() {
	}

	/**
	 * I don't want to inject DBManger everywhere I use it.<br>
	 * It's inconvenience and has to add configure to scan whole platform
	 * package.<br>
	 * So obviously I create a new one here.
	 * 
	 * @return
	 */
	public static DBManager getInstance() {
		if (instance == null) {
			instance = new DBManager();
			if (CommonUtil.isUnitTest) {
				@SuppressWarnings("resource")
				ApplicationContext appCtx = new ClassPathXmlApplicationContext(
						"springJdbcContext.xml");
				instance.setHibernateTemplate((HibernateTemplate) appCtx
						.getBean("hibernateTemplate"));
			} else {
				instance.setHibernateTemplate((HibernateTemplate) AppCtx
						.getCtx().getBean("hibernateTemplate"));
			}

		}
		return instance;
	}

	public List<Stock> findAllStocks() {
		try {
			logger.info("Start find all stocks");
			return getHibernateTemplate().loadAll(Stock.class);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return new ArrayList<Stock>();
	}

	public List<Task> findAllTasks() {
		try {
			logger.info("Start find all tasks");
			return getHibernateTemplate().loadAll(Task.class);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return new ArrayList<Task>();
	}

	public BaseEntity saveOrUpdate(final BaseEntity bean) {
		try {
			bean.setCreationSystemColumns();
			getHibernateTemplate().saveOrUpdate(bean);
		} catch (Exception e) {
			throw new RuntimeException("Cannot save or update bean with id: "
					+ bean.getId());
		}

		return bean;
	}

	public void saveOrUpdateAll(final List<BaseEntity> beans) {
		try {
			for (BaseEntity bean : beans) {
				bean.setCreationSystemColumns();
			}
			getHibernateTemplate().saveOrUpdateAll(beans);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void bulkSaveOrUpdate(final List<BaseEntity> beans) {
		try {
			for (BaseEntity bean : beans) {
				bean.setCreationSystemColumns();
			}
			
			getHibernateTemplate().executeWithNativeSession(
				new HibernateCallback<Object>() {
					public Object doInHibernate(Session session) throws HibernateException {
						session.setFlushMode(FlushMode.MANUAL);
								
						Transaction tx = session.beginTransaction(); 
						int i = 0;
						for (BaseEntity entity : beans) {
							session.saveOrUpdate(entity);
									
							i++;
							if(i % 10000 == 0) {
							   	session.flush();
							 	session.clear();
							}
						}
						tx.commit(); 
						return null;
					}
				});
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Transactional
	public void updateLastTransday() throws Exception {
		getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException {
				session.createSQLQuery(DaoQueryConsts.SP_UPDATE_LAST_TRANSDAY).executeUpdate();
				return null;
			}
		});
	}

}
